#ifndef __SCENE_GRAPH_H__
#define __SCENE_GRAPH_H__

#pragma once

#include "SceneNode.h"
#include "RenderObject.h"

namespace Nezha
{

	class RenderTarget;
	class Camera;
	class RenderQueue;

	/** param container. */
	class GRAPHICS_ENTRY SceneObjectCreation
	{
	public:
		SceneObjectCreation(int sot) :mSceneObjectType(sot) {}
		virtual ~SceneObjectCreation(){}

		int getObjectTypeInteger() const
		{
			return mSceneObjectType;
		}

	private:
		int mSceneObjectType;
	};

	class GRAPHICS_ENTRY SceneObjectFactory
	{
	public:
		SceneObjectFactory()
			:mSceneObjectType(0)
		{
		}
		virtual ~SceneObjectFactory(){}

		SceneObject* create(const _string& name, SceneObjectCreation* params)
		{
			if(params && params->getObjectTypeInteger() == getObjectTypeInteger())
				return createImpl(name, params);

			return NULL;
		}

		const _string& getObjectType() const
		{
			return mTypeName;
		}

		int getObjectTypeInteger() const
		{
			return mSceneObjectType;
		}

	protected:
		virtual SceneObject* createImpl(const _string& name, SceneObjectCreation* params) = 0;

		_string mTypeName;
		int mSceneObjectType;
	};

	/** Scene manager base.
	*/
	class GRAPHICS_ENTRY SceneGraph
	{
	public:
		virtual ~SceneGraph();

		virtual SceneNode* createSceneNode(const _string& name, SceneNode* parent) = 0;

		virtual SceneNode* findSceneNode(const _string& name) = 0;

		virtual void destroySceneNode(SceneNode* sn) = 0;

		SceneObject* createFromScript(const _string& name, const _string& scriptName, SceneNode* bindNode = NULL);

		virtual SceneObject* create(const _string& name, SceneObjectCreation* params, SceneNode* bindNode = NULL) = 0;

		Camera* createCamera(const _string& name, bool perspective);

		Camera* getCamera(const _string& name);

		void destroyCamera(Camera* cam);

		// built in objects
		SceneObject* createBuiltInBox(const _string& name);

		SceneObject* createBuiltInPlane(const _string& name, u16 xExtent, u16 yExtent);

		SceneObject* createBuiltInSphere(const _string& name);

		virtual SceneObject* findObject(const _string& name) = 0;

		/**
		@REMARKS
			all its children will be destroyed.
		*/
		virtual void destroySceneObject(SceneObject* so) = 0;

		virtual SceneObjectFactory* getFactoryByTypeInt(int sot) = 0;

		virtual void setSceneObjectFactory(SceneObjectFactory* fac) = 0;

		virtual void update(float dt) = 0;

		/** Scene graph need push all the visible render objects to corresponding render queue.
			Lights that not overlapped with camera's frustum should be culled.
		@remarks
			Currenlty we consider forward shading only.
		*/
		virtual void cull(RenderTarget* rt, RenderQueue* opaRq, RenderQueue* transRq);

		const SceneNodePtr& getRootNode() const
		{
			return mRootNode;
		}

		static const _string& SceneObjectTypeString(SceneObjectType sot);

		SceneGraph();
	protected:
		virtual SceneObjectCreation* loadScript(const _string& scriptName) = 0;

		virtual void _insertObject(SceneObject* so) = 0;

		void _cullByCamera(Camera* cam, SceneNode* sn, RenderQueue* opaRq, RenderQueue* transRq);

		SceneNodePtr mRootNode;

	private:
		SceneObjectFactory* mBulitInMeshCreator;
	};

	class GRAPHICS_ENTRY SceneGraphFactory
	{
	public:
		virtual ~SceneGraphFactory(){}

		virtual SceneGraph* create() = 0;
	};

}//end namespace Nezha

#endif //end __SCENE_GRAPH_H__